package com.rainbow.support.minio.toolkit;

import com.rainbow.support.minio.domain.FileDetail;
import io.minio.MinioClient;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * minio对象存储工具
 * @author 付为地
 * 参考资料:http://docs.minio.org.cn/docs/master/java-client-api-reference
 */
@Slf4j
@AllArgsConstructor
public class MinioUtil {

    private MinioClient minioClient;

    public static final int MIN_MULTIPART_SIZE = 5242880;

    /**
     * 创建一个桶
     * @param bucket 桶名称
     */
    @SneakyThrows
    public void createBucket(String bucket){
        boolean found = minioClient.bucketExists(bucket);
        if (!found) {
            minioClient.makeBucket(bucket);
        }
    }

    /**
     * 列出所有桶的名称
     * @return 所有桶的名称
     */
    @SneakyThrows
    public List<String> listBucketsName(){
        List<String> names =minioClient.listBuckets().stream()
                .filter(t-> ObjectUtils.isNotEmpty(t)&&ObjectUtils.isNotEmpty(t.name()))
                .map(p->p.name()).distinct().collect(Collectors.toList());
        return names;
    }

    /**
     * 列出所有桶
     * @return 所有桶信息
     */
    @SneakyThrows
    public List<Bucket> listBuckets(){
        return minioClient.listBuckets();
    }

    /**
     * 获取当前桶
     * @param bucket 桶名称
     * @return 返回桶信息
     */
    @SneakyThrows
    public Bucket getBucket(String bucket) {
        Optional<Bucket> optBucket = minioClient.listBuckets().stream().filter(b -> Objects.equals(b.name(),bucket)).findFirst();
        if (ObjectUtils.isNotEmpty(optBucket) && optBucket.isPresent()) {
            return optBucket.get();
        }
        return null;
    }

    /**
     * 获取桶文件
     * @param bucket 桶的名称
     * @param objectName 对象名称
     * @return 文件路径
     */
    @SneakyThrows
    public String getFileUrl(String bucket,String objectName){
        return minioClient.getObjectUrl(bucket,objectName);
    }


    /**
     * 获取文件零时路径默认7天
     * @param bucket 桶的名称
     * @param objectName 对象名称
     * @param expire 过期时间
     * @return 文件路径
     */
    @SneakyThrows
    public String getShortFileUrl(String bucket,String objectName,Integer expire){
        if(ObjectUtils.isEmpty(expire)){
            return minioClient.presignedGetObject(bucket,objectName);
        }
        return minioClient.presignedGetObject(bucket,objectName,expire);
    }


    /**
     * 列出一个桶中的所有文件和目录
     * @param bucket 桶的名称
     * @return 桶种所有文件和目录
     */
    @SneakyThrows
    public List<FileDetail> listFiles(String bucket){
        Iterable<Result<Item>> results = minioClient.listObjects(bucket);
        List<FileDetail> infos = new ArrayList<>();
        results.forEach(r -> {
            try {
                FileDetail info = new FileDetail();
                Item item = r.get();
                info.setFilename(item.objectName());
                info.setDirectory(item.isDir());
                infos.add(info);
            } catch (Exception e) {
                log.error("minio获取文件异常:",e);
            }
        });
        return infos;
    }

    /**
     * 删除一个桶
     * @param bucket 桶的名称
     */
    @SneakyThrows
    public void deleteBucket(String bucket){
        minioClient.removeBucket(bucket);
    }

    /**
     * 删除桶的一个对象
     * @param bucket 桶的名称
     * @param objectName 对象名称
     */
    @SneakyThrows
    public void deleteObject(String bucket, String objectName){
        minioClient.removeObject(bucket,objectName);
    }

    /**
     * 上传文件
     * @param file 文件
     * @param bucket 桶
     */
    @SneakyThrows
    public String upload(MultipartFile file, String bucket){
        return upload(file,bucket,true);
    }


    /**
     * 上传文件
     * @param file 文件
     * @param bucket 桶
     * @param useDir 配置文件夹
     */
    @SneakyThrows
    public String upload(MultipartFile file, String bucket,Boolean useDir){
        if(useDir){
            String name = convertFileName(file.getOriginalFilename());
            createBucket(bucket);
            upload(file, bucket, name);
            return getShortFileUrl(bucket, name, 60 * 60 * 24*7);
        }else{
            upload(file, bucket, file.getOriginalFilename());
            return getFileUrl(bucket, file.getOriginalFilename());
        }
    }


    /**
     * 上传文件
     * @param inputStream 文件流
     * @param bucket 桶
     * @param useDir 配置文件夹
     */
    @SneakyThrows
    public String upload(InputStream inputStream,String contentType, String bucket,String objectName,Boolean useDir){
        if(useDir){
            String name = convertFileName(objectName);
            createBucket(bucket);
            upload(inputStream,contentType,bucket, name);
            return getShortFileUrl(bucket, name, 60 * 60 * 24*7);
        }else{
            upload(inputStream,contentType,bucket, objectName);
            return getFileUrl(bucket, objectName);
        }
    }

    /**
     * 上传一个文件到桶种
     * @param file 上传的文件
     * @param bucket 桶名称
     * @param objectName 对象名称
     */
    @SneakyThrows
    public void upload(MultipartFile file, String bucket, String objectName){
        PutObjectOptions putObjectOptions = new PutObjectOptions(file.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
        putObjectOptions.setContentType(file.getContentType());
        minioClient.putObject(bucket, objectName, file.getInputStream(), putObjectOptions);
    }

    /**
     * 上传一个文件到桶种
     * @param inputStream 文件流
     * @param contentType 文件类型
     * @param bucket 桶名称
     * @param objectName 对象名称
     */
    @SneakyThrows
    public void upload(InputStream inputStream,String contentType,String bucket, String objectName){
        PutObjectOptions putObjectOptions = new PutObjectOptions(inputStream.available(),PutObjectOptions.MIN_MULTIPART_SIZE);
        putObjectOptions.setContentType(contentType);
        minioClient.putObject(bucket, objectName, inputStream, putObjectOptions);
    }

    /**
     * 下载桶的一个文件
     * @param bucket 桶的名称
     * @param objectName 对象名称
     * @return 返回桶的文件流
     */
    public InputStream download(String bucket, String objectName){
        try {
            return minioClient.getObject(bucket,objectName);
        } catch (Exception e) {
            log.error("下载桶[{}],文件[{}]时异常:",bucket,objectName,e);
            return null;
        }
    }

    /**
     * 断点下载桶的一个文件
     * @param bucket 桶的名称
     * @param objectName 对象名称
     * @param offset 是起始字节的位置
     * @param length 是要读取的长度 (可选，如果无值则代表读到文件结尾)
     * @return 返回桶的文件流
     */
    public InputStream download(String bucket, String objectName,long offset, Long length){
        try {
            return minioClient.getObject(bucket,objectName,offset,length);
        } catch (Exception e) {
            log.error("断点下载桶[{}],文件[{}]时异常:",bucket,objectName,e);
            return null;
        }
    }

    /**
     * 判断对象是否存在
     * @param bucket
     * @param objectName
     * @return
     */
    public Boolean exists(String bucket, String objectName){
        try {
            minioClient.statObject(bucket,objectName);
            return true;
        } catch (Exception e) {
            log.error("检查桶对象异常:",e);
            return false;
        }
    }

    /**
     * 转换文件地址
     * @param sourceName 源文件名称
     * @return 时间戳文件名
     */
    private String convertFileName(String sourceName) {
        Date now = new Date();
        String folder = DateFormatUtils.format(now, "yyyyMMdd");
        String prefix = DateFormatUtils.format(now, "yyyyMMddHHmmssS");
        String fileName = sourceName.substring(0, sourceName.lastIndexOf(".")) + "_" + prefix + sourceName.substring(sourceName.lastIndexOf("."));
        return folder + "/" + fileName;
    }

}
